package com.aratek.salary.service;

import com.aratek.salary.common.PoiUtils;
import com.aratek.salary.controller.RRException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Yvan Jiang
 * @version 0.0.1
 * @Title SalaryReportSplitter
 * @note 报告分割器
 * @note Copyright 2016 by Aratek . All rights reserved
 * @time 2017/11/16
 **/
public class SalaryReportSplitter {
    private static final Logger LOG = LoggerFactory.getLogger(SalaryReportSplitter.class);
    private final File srcFile;
    private final File template;
    private final String tarFilePos;
    private static final ArrayBlockingQueue<Map<String,File>> TEMP_FILE_LIST = new ArrayBlockingQueue<>(500);
    private static final ExecutorService DISPATCHER = Executors.newSingleThreadExecutor(r -> new Thread(r,"DISPATCHER"));
    private static final AtomicInteger TASK_COUNT = new AtomicInteger(0);

    private static final ExecutorService POOR = new ThreadPoolExecutor(5, 20, 50,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(500), new ThreadFactory() {
        private AtomicInteger count = new AtomicInteger(0);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r,"XmlFileCreator-"+count.getAndIncrement());
        }
    });

    public static SalaryReportSplitter instance(File file,String filePath) {
        if(TASK_COUNT.get() != 0){
            throw new RRException("Work still online");
        }
        return new SalaryReportSplitter(file, filePath);
    }

    private SalaryReportSplitter(File file,String filePath) {
        this.srcFile = file;
        String tempPath = FileUtils.getTempDirectoryPath();
        template = FileUtils.getFile(filePath);
        if(tempPath.endsWith(File.separator)){
            tarFilePos = tempPath.substring(0,tempPath.length()-1);
        }else {
            tarFilePos = tempPath;
        }
    }

    /**
     *
     * @return 总记录条数
     */
    public int dispatch() {
        Workbook workbook = null;
        int size = 0;
        boolean flag = false;
        try {
            workbook = createWorkbook(srcFile);
            Sheet sheet = workbook.getSheetAt(0);
            for (int i = 0; i <= sheet.getLastRowNum() - 3; i++) {
                //从第三行开始读取实际数据
                Row data = sheet.getRow(i + 3);
                if (data == null) {
                    break;
                }
                //识别姓名，增加月份，创建新模板文件
                Cell cell = data.getCell(40);
                if (cell == null || cell.getRichStringCellValue() == null) {
                    LOG.warn("row {},email error", i);
                    Worker.getErrorList().add("第"+i+"行无法识别email地址");
                    continue;
                }
                cell = data.getCell(2);
                if (cell == null || cell.getRichStringCellValue() == null) {
                    LOG.warn("row {},name error", i);
                    Worker.getErrorList().add("第"+i+"行无法识别姓名");
                    continue;
                }
                size++;
            }
            flag = true;
        } catch (IOException | InvalidFormatException | EncryptedDocumentException e) {
            LOG.error("FILE DISPATCH ERROR", e);
        } finally {
            IOUtils.closeQuietly(workbook);
            if(flag){
                DISPATCHER.submit(() -> {
                    readRowData(srcFile, tarFilePos, template);
                });
            }
        }
        return size;
    }

    private static Workbook createWorkbook(File srcFile) throws IOException, InvalidFormatException {
        String extention = FilenameUtils.getExtension(srcFile.getName()).toLowerCase();
        Workbook workbook;
        if("xlsx".equals(extention)) {
            workbook = new XSSFWorkbook(srcFile);
        }else if("xls".equals(extention)){
            workbook = new HSSFWorkbook(new POIFSFileSystem(srcFile));
        }else {
            throw new RRException("源文件后缀名非法");
        }
        return workbook;
    }

    private static FormulaEvaluator createFormulaEvaluator(Workbook workbook){
        if(workbook instanceof XSSFWorkbook){
            return new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
        }else if (workbook instanceof HSSFWorkbook){
            return new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
        }else {
            throw new RRException("不支持该格式的文件");
        }
    }

    private static void readRowData(File srcFile, String tarFilePos, File template) {
        TASK_COUNT.getAndIncrement();
        Workbook workbook = null;
        FormulaEvaluator formulaEvaluator = null;
        try {
            workbook = createWorkbook(srcFile);
            formulaEvaluator = createFormulaEvaluator(workbook);
            Sheet sheet1 = workbook.getSheetAt(0);
            int count = sheet1.getLastRowNum();
            for (int i = 0; i <= count-3; i++) {
                //从第三行开始读取实际数据
                Row data = sheet1.getRow(i + 3);
                if (data == null) {
                    break;
                }
                //识别姓名，增加月份，创建新模板文件
                Cell cell = data.getCell(40);
                if (cell == null || cell.getRichStringCellValue() == null) {
                    continue;
                }
                String emailAddress = cell.getRichStringCellValue().getString();
                cell = data.getCell(2);
                if (cell == null || cell.getRichStringCellValue() == null) {
                    continue;
                }
                String filename = cell.getRichStringCellValue().getString();
                filename = i + "_" + LocalDate.now().minusMonths(1).getMonthValue() + "月工资条" + filename;
                filename = tarFilePos + File.separator + filename + ".xlsx";
                ZipSecureFile.setMinInflateRatio(0.0d);
                File templateCopy = File.createTempFile("salTemp","i.xlsx");
                FileUtils.copyToFile(new FileInputStream(template),templateCopy);
                copyNewFile(templateCopy, data, formulaEvaluator, emailAddress, filename);
            }
        } catch (Throwable e) {
            LOG.error("FILE DISPATCH ERROR", e);
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(workbook);
            TASK_COUNT.getAndDecrement();
        }
    }

    private static void copyNewFile(File template, Row data,FormulaEvaluator formulaEvaluator, String emailAddress, String filename) throws IOException, InvalidFormatException {
        //将数据写入其中
        if(!template.exists()){
            throw new RRException("File not exist");
        }
        Workbook outputWorkBook = new XSSFWorkbook(template);
        Sheet resSheet = outputWorkBook.getSheetAt(0);
        PoiUtils.copyRow(outputWorkBook, data,formulaEvaluator, resSheet.getRow(3), true);
        POOR.submit(new XlsFileWriter(emailAddress, filename, outputWorkBook,template));
    }

    public static ArrayBlockingQueue<Map<String, File>> getExcelFileList() {
        return TEMP_FILE_LIST;
    }

    public static class XlsFileWriter implements Runnable{
        private String email;
        private String filename;
        private Workbook outputWorkBook;
        private File template;

        public XlsFileWriter(String email,String file,Workbook outputWorkBook,File template) {
            TASK_COUNT.getAndIncrement();
            this.email = email;
            this.filename = file;
            this.outputWorkBook = outputWorkBook;
            this.template= template;
        }

        @Override
        public void run() {
            FileOutputStream out = null;
            try {
                File file = new File(filename);
                out = new FileOutputStream(file);
                outputWorkBook.write(out);
                Map<String,File> map = new HashMap<>(1);
                map.put(email, file);
                TEMP_FILE_LIST.add(map);
            } catch (IOException e) {
                LOG.error("File create error",e);
            }finally {
                IOUtils.closeQuietly(out);
                IOUtils.closeQuietly(outputWorkBook);
                FileUtils.deleteQuietly(template);
                TASK_COUNT.getAndDecrement();
            }
        }
    }

    public static boolean isWorking(){
        LOG.debug("count {}",TASK_COUNT.get());
        return TASK_COUNT.get() != 0;
    }

    public static void destroy(){
        POOR.shutdownNow();
        DISPATCHER.shutdownNow();
    }

    public void deleteSrcFile(){
        FileUtils.deleteQuietly(srcFile);
    }
}
